/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is dual-licensed under either the MIT license found in the
 * LICENSE-MIT file in the root directory of this source tree or the Apache
 * License, Version 2.0 found in the LICENSE-APACHE file in the root directory
 * of this source tree. You may select, at your option, one of the
 * above-listed licenses.
 */

package com.facebook.buck.jvm.kotlin;

import static com.facebook.buck.jvm.java.JavaPaths.SRC_ZIP;

import com.facebook.buck.core.filesystems.AbsPath;
import com.facebook.buck.core.filesystems.RelPath;
import com.facebook.buck.jvm.cd.command.kotlin.KotlinExtraParams;
import com.facebook.buck.jvm.cd.command.kotlin.LanguageVersion;
import com.facebook.buck.jvm.core.BuildTargetValue;
import com.facebook.buck.jvm.core.BuildTargetValueExtraParams;
import com.facebook.buck.jvm.java.CompilerParameters;
import com.facebook.buck.jvm.java.JarParameters;
import com.facebook.buck.jvm.java.RemoveClassesPatternsMatcher;
import com.facebook.buck.jvm.kotlin.cd.analytics.KotlinCDAnalytics;
import com.facebook.buck.jvm.kotlin.kotlinc.Kotlinc;
import com.facebook.buck.step.isolatedsteps.IsolatedStep;
import com.facebook.buck.step.isolatedsteps.common.MakeCleanDirectoryIsolatedStep;
import com.facebook.buck.step.isolatedsteps.common.ZipIsolatedStep;
import com.facebook.buck.step.isolatedsteps.java.JarDirectoryStep;
import com.facebook.buck.util.zip.ZipCompressionLevel;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSortedSet;
import java.nio.file.Path;
import java.util.Map;
import java.util.Optional;
import java.util.logging.Level;

public class KosabiStubgenStepsBuilder {
  private static final String VERBOSE = "-verbose";

  public static void prepareKosabiStubgenIfNeeded(
      RelPath buckOut,
      AbsPath buildCellRootPath,
      BuildTargetValue invokingRule,
      CompilerParameters parameters,
      ImmutableList.Builder<IsolatedStep> steps,
      KotlinExtraParams extraParams,
      BuildTargetValueExtraParams buildTargetValueExtraParams,
      ImmutableSortedSet.Builder<RelPath> sourceWithStubsOutputBuilder,
      ImmutableSortedSet.Builder<RelPath> sourceWithKSPOutputBuilder,
      RelPath outputDirectory,
      ImmutableSortedSet<RelPath> sourceFilePaths,
      Path pathToSrcsList,
      ImmutableList<AbsPath> allClasspaths,
      RelPath reportsOutput,
      Kotlinc kotlinc,
      ImmutableMap<String, AbsPath> allKosabiPluginOptionPath,
      ImmutableList.Builder<AbsPath> sourceOnlyAbiClasspathBuilder,
      ImmutableList.Builder<IsolatedStep> postKotlinCompilationFailureSteps,
      KotlinCDAnalytics kotlinCDAnalytics,
      LanguageVersion languageVersion) {
    ImmutableSortedSet<RelPath> stubsGenOutputPath;
    if (invokingRule.isSourceOnlyAbi()) {

      if (allKosabiPluginOptionPath.isEmpty()) {
        throw new RuntimeException(
            "Building Kotlin SourceOnlyAbi without Kosabi plugins setup, please check if Kosabi is"
                + " turn off (kotlin.enable_source_only_abi == false), or remove"
                + " abi_generation_mode=\"source_only\" from the target.");
      }

      RelPath stubgenOutputDir = buildTargetValueExtraParams.getGenPath("__%s_stubgen_stubs__");
      RelPath stubgenClassOutputDir =
          buildTargetValueExtraParams.getGenPath("__%s_stubgen_stubs_class__");

      RelPath stubsJar = buildTargetValueExtraParams.getGenPath("__%s_stubgen_stubs.jar");
      sourceOnlyAbiClasspathBuilder.add(stubsJar.toAbsolutePath());

      RelPath stubsOutputZipDir =
          buildTargetValueExtraParams.getGenPath("__%s_stubgen_stubs_zip__");

      // As we don't know all the files that will be generated by stubgen, we need to have a zip
      // step to archive the stubs to get a deterministic path, so that we can use it as
      // input for further compilation step.
      RelPath stubsZip =
          buildTargetValueExtraParams.getGenPath("__%s_stubgen_stubs_zip__/stubs" + SRC_ZIP);

      ImmutableSortedSet.Builder<RelPath> stubgenSrcPathBuilder = null;
      stubgenSrcPathBuilder = ImmutableSortedSet.orderedBy(RelPath.comparator());
      stubgenSrcPathBuilder.add(stubsZip);
      stubsGenOutputPath = stubgenSrcPathBuilder.build();
      sourceWithStubsOutputBuilder.addAll(stubsGenOutputPath);
      sourceWithKSPOutputBuilder.addAll(stubsGenOutputPath);

      steps.addAll(MakeCleanDirectoryIsolatedStep.of(stubgenOutputDir));
      steps.addAll(MakeCleanDirectoryIsolatedStep.of(stubsOutputZipDir));

      steps.add(
          new KosabiStubgenStep(
              invokingRule,
              outputDirectory.getPath(),
              sourceFilePaths,
              pathToSrcsList,
              allClasspaths,
              extraParams.getKotlinHomeLibraries(),
              reportsOutput,
              kotlinc,
              ImmutableList.of(),
              ImmutableList.of(VERBOSE),
              parameters.getOutputPaths(),
              parameters.getShouldTrackClassUsage(),
              buckOut,
              allKosabiPluginOptionPath.entrySet().stream()
                  .filter(
                      entry ->
                          (KosabiConfig.PROPERTY_KOSABI_STUBS_GEN_PLUGIN.equals(entry.getKey())
                              || KosabiConfig.PROPERTY_KOSABI_STUBS_GEN_K2_PLUGIN.equals(
                                  entry.getKey())))
                  .collect(ImmutableMap.toImmutableMap(Map.Entry::getKey, Map.Entry::getValue)),
              "Terminating compilation. We're done with Stubgen.",
              false,
              sourceOnlyAbiClasspathBuilder.build(),
              extraParams.getShouldVerifySourceOnlyAbiConstraints(),
              postKotlinCompilationFailureSteps.build(),
              extraParams.getDepTrackerPlugin(),
              stubgenOutputDir,
              stubgenClassOutputDir,
              kotlinCDAnalytics,
              languageVersion));

      steps.add(
          new ZipIsolatedStep(
              buildCellRootPath,
              stubsZip.getPath(),
              ImmutableSet.of(),
              ImmutableSet.of(),
              false,
              ZipCompressionLevel.DEFAULT,
              stubgenOutputDir.getPath()));

      steps.add(
          new JarDirectoryStep(
              new JarParameters(
                  false,
                  true,
                  stubsJar,
                  RemoveClassesPatternsMatcher.EMPTY,
                  ImmutableSortedSet.orderedBy(RelPath.comparator())
                      .add(stubgenClassOutputDir)
                      .build(),
                  ImmutableSortedSet.of(),
                  Optional.empty(),
                  Optional.empty(),
                  Level.INFO)));
    }
  }
}
